#include"ArenaRoom.hpp"

#include "peony/base/Timestamp.hpp"
#include "peony/hybrid/google/ProtoCallback.hpp"
#include "net/TcpConnector.hpp"

#include "proto/MsgBattle.pb.h"
#include "proto/MsgTimer.pb.h"

#include "GameSetting.hpp"
using namespace ProtoMsg;
using namespace std;


ArenaRoom::ArenaRoom(ArenaRoomID id,string name):
	_id(id),
	_name(name),
    _frameNo(0)
{
    _updateTimems = Timestamp::Nowms();
	_overTimems = _updateTimems + GAME_DURATION;  
    printf("ArenaRoom construct id = %u name = %s\n",_id,_name.c_str());
}
ArenaRoom::~ArenaRoom()
{
    printf("ArenaRoom destruct id = %u name = %s\n",_id,_name.c_str());

	ProtoMsg::GameOver notify;
	for (auto iter : _ballerDict)
	{
		Baller* p = iter.second;
		p->SendMessage(notify,0);
		p->ResetBallerState();
	}
	
}

bool ArenaRoom::UpdateRoom()
{
    //发送帧数据
    bool isOver = broadcastFrame();
//    if(!isOver)
//    {//更新食物
//        _foodMgr.CreateFood();
//    }
    return isOver;
}

int64_t ArenaRoom::deltaTime(int64_t nowms)
{
    int64_t intervalms = nowms - _updateTimems;
    _updateTimems = nowms;
    return intervalms;
}

bool ArenaRoom::isTimeOver(int64_t nowms)
{
	return nowms >= _overTimems;	
}

bool ArenaRoom::broadcastFrame()
{
	// printf("RoomID: %u name = %s 广播 第 %d 帧数据\n",_id,_name.c_str(),_frameNo);
	// if(_frameNo > FINAL_FRAME_NO)
	// {
		// return true;
	// }

    int64_t nowms = Timestamp::Nowms();
	
	if(isTimeOver(nowms))
		return true;

	int64_t deltams = deltaTime(nowms);
    ProtoMsg::PerFrameData message;
	Dto_Baller* one = message.add_ballers();
	for(auto iter : _ballerDict)
	{
		//根据玩家操作计算出当前状态
		iter.second->HandleOperation(deltams);
		iter.second->fillBallInfo(one);
	}
	message.set_frameno(_frameNo);
	message.set_timestampms(nowms);

	BroadcastMessage<PerFrameData>(message);

	++_frameNo;

	return false;
}


bool ArenaRoom::IsExistBaller(BallerID id)
{
	return _ballerDict.find(id) != _ballerDict.end();
}

bool ArenaRoom::BallerJoin(Baller* baller)
{
	if(baller == nullptr)
	{
		return false;
	}

	if(baller->IsJoinArenaRoom())
	{
		return false;
	}

	if(IsExistBaller(baller->ID()))
	{
		return false;
	}
	//初始化球状态
	baller->InitBallerState();

	BallerJoinNotify	notify;
	baller->fillBallInfo(notify.mutable_newballer());

	for(auto iter : _ballerDict)
	{
		Baller* one = iter.second;
		one->SendMessage<BallerJoinNotify>(notify,0);
	}
	_ballerDict[baller->ID()] = baller;
	baller->SetArenaRoom(_id);
	return true;
}

void ArenaRoom::BallerExit(Baller* baller)
{
	if(baller == nullptr)
	{
		return;
	}

	if(!baller->IsJoinArenaRoom())
	{
		return;
	}

	if(baller->RoomID() != _id)
	{
		return;
	}	

	auto ballerIter = _ballerDict.find(baller->ID());
	if(ballerIter != _ballerDict.end())
	{
		baller->SetArenaRoom(-1);
		_ballerDict.erase(ballerIter);
	}

	BallerExitNotify	notify;
	baller->fillUserInfo(notify.mutable_exiteduser());
	for(auto iter : _ballerDict)
	{
		Baller* one = iter.second;
		one->SendMessage<BallerExitNotify>(notify,0);
	}
}

uint32_t ArenaRoom::NumOfBaller()
{
	return (uint32_t)_ballerDict.size();
}

void ArenaRoom::fillDto_ArenaRoomInfo(ProtoMsg::Dto_ArenaRoomInfo* pArenaRoom)
{
    if(pArenaRoom == nullptr)return;

    pArenaRoom->set_roomid(_id);
    pArenaRoom->set_roomname(_name);

    auto iterBaller = _ballerDict.begin();
    for (;iterBaller != _ballerDict.end();iterBaller++)
    {
        Baller* baller = iterBaller->second;
        Dto_Baller* one = pArenaRoom->add_users();
        baller->fillBallInfo(one);
    }
}

void ArenaRoom::fillDto_ArenaBrief(ProtoMsg::Dto_ArenaBrief* brief)
{
    if(brief == nullptr)return;
    brief->set_roomid(_id);
    brief->set_roomname(_name);
    brief->set_ballernum(NumOfBaller());

}

//-------------------------------------------------------------

ArenaRoomID     ArenaRoomMgr::RoomIDIndex   = 0;
ArenaRoomMgr*   ArenaRoomMgr::Instance      = nullptr;

ArenaRoomMgr& ArenaRoomMgr::GetInstance()
{
    if(Instance == nullptr)
    {
        Instance = new ArenaRoomMgr();
    }
    return *Instance;
}

ArenaRoomPtr ArenaRoomMgr::CreateRoom(std::string name,Baller* creator)
{
    if(creator == nullptr)return nullptr;

    ArenaRoomID newRoomID = ArenaRoomMgr::RoomIDIndex++;

    auto newRoomPtr = ArenaRoomPtr(new ArenaRoom(newRoomID,name));
    newRoomPtr->BallerJoin(creator);

    _roomDict[newRoomID] = newRoomPtr;

    return newRoomPtr;
}

ArenaRoomPtr ArenaRoomMgr::QueryRoom(ArenaRoomID id)
{
    auto roomIter = _roomDict.find(id);
    if(roomIter != _roomDict.end())
    {
        return roomIter->second;
    }
    return nullptr;
}

void ArenaRoomMgr::RemoveRoom(ArenaRoomID id)
{
    auto roomIter = _roomDict.find(id);
    if(roomIter != _roomDict.end())
    {
        _roomDict.erase(roomIter);
    }
}

void ArenaRoomMgr::BallerExitRoom(BallerID id)
{
    Baller* baller = BallerMgr::GetInstance().QueryBaller(id);
    BallerExitRoom(baller);
}

void ArenaRoomMgr::BallerExitRoom(Baller* baller)
{
	if(baller == nullptr)
	{
		return;
	}

	if(!baller->IsJoinArenaRoom())
	{
		return;
	}
	
	ArenaRoomPtr roomPtr = QueryRoom(baller->RoomID());
	if(roomPtr == nullptr)
	{
		return;
	}

	roomPtr->BallerExit(baller);
}

void ArenaRoomMgr::UpdateAllRoom()
{
	for(auto roomIter = _roomDict.begin(); roomIter != _roomDict.end();)
	{
		ArenaRoomPtr roomPtr = roomIter->second;
		bool isFinish = roomPtr->UpdateRoom();
		if(isFinish)
        {
            roomIter = _roomDict.erase(roomIter);
        }
        else
        {
            roomIter++;
        }
	}
}
using namespace ProtoMsg;
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
TcpConnectorPtr SystemNoThing(TcpConnectorPtr conn)
{
    return conn;
}
#define HANDLE_SYSTEM_MSG(REQUEST)\
void Handle##REQUEST(TcpConnectorPtr conn,const REQUEST& request);\
template<>\
ProtoCallbackT<REQUEST> ProtoCallbackT<REQUEST>::Instance(Handle##REQUEST,SystemNoThing);\
void Handle##REQUEST(TcpConnectorPtr conn,const REQUEST& request)\
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Baller* ConvertBaller(TcpConnectorPtr conn)
{
    return (Baller*)conn->GetTie();
}

#define HANDLE_BALLER_MSG(REQUEST)\
void HandleBaller##REQUEST(Baller* baller,const REQUEST& request);\
template<>\
ProtoCallbackT<REQUEST,Baller*> ProtoCallbackT<REQUEST,Baller*>::Instance(HandleBaller##REQUEST,ConvertBaller);\
void HandleBaller##REQUEST(Baller* baller,const REQUEST& request)


#define REPLY_BALLER_MSG(REQUEST,RESPONSE)\
uint32_t Reply##REQUEST(Baller* baller,const REQUEST& request,RESPONSE& response);\
template<>\
ProtoCallbackReply<REQUEST,RESPONSE,Baller*> ProtoCallbackReply<REQUEST,RESPONSE,Baller*>::Instance(Reply##REQUEST,ConvertBaller);\
uint32_t Reply##REQUEST(Baller* baller,const REQUEST& request,RESPONSE& response)
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

HANDLE_SYSTEM_MSG(FrameTimer)
{
	ArenaRoomMgr::GetInstance().UpdateAllRoom();
}

REPLY_BALLER_MSG(CreateRoomCS,CreateRoomSC)
{
    ArenaRoomPtr roomPtr = ArenaRoomMgr::GetInstance().CreateRoom(request.roomname(),baller);
    roomPtr->fillDto_ArenaRoomInfo(response.mutable_roominfo());
    return 0;
}

REPLY_BALLER_MSG(PullRoomInfoCS,PullRoomInfoSC)
{
    const RoomDict& roomDict = ArenaRoomMgr::GetInstance().GetRoomDict();
    for(auto one : roomDict)
    {
        ArenaRoomPtr room = one.second;
        Dto_ArenaBrief* roomInfo = response.add_roomlist();
        room->fillDto_ArenaBrief(roomInfo);
    }
    return 0;
}

REPLY_BALLER_MSG(JoinRoomInfoCS,JoinRoomInfoSC)
{
    ArenaRoomID joinRoomID = request.roomid();
    auto roomPtr = ArenaRoomMgr::GetInstance().QueryRoom(joinRoomID);
    if(roomPtr == nullptr)
    {
        return -1;
    }

    if(!roomPtr->BallerJoin(baller))
    {
        return -2;
    }

    roomPtr->fillDto_ArenaRoomInfo(response.mutable_roominfo());

    return 0;
}


HANDLE_BALLER_MSG(BallerOperation)
{
    baller->SaveOperation(request);
}
